package com.tony.imageeffects;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;

import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;

import com.tony.imageeffects.effects.Brick;
import com.tony.imageeffects.effects.Effects;
import com.tony.imageeffects.effects.Light;
import com.tony.imageeffects.effects.Old;
import com.tony.imageeffects.effects.Pixelate;
import com.tony.imageeffects.effects.Relief;
import com.tony.imageeffects.effects.Television;
import com.tony.imageeffects.hjlabs.OffsetXY;
import com.tony.imageeffects.hjlabs.PointillizeFilter;
import com.tony.imageeffects.hjlabs.RippleFilter;
import com.tony.imageeffects.hjlabs.TwirlFilter;

public class MainActivity extends Activity implements OnClickListener, OnTouchListener {

	public static final int REQUEST_CODE_LOAD_IMAGE = 6996;

	// public static final int EF_POINTILLIZE = 1001;
	// public static final int EF_GRAYSCALE = 1002;
	// public static final int EF_PIXELIZE = 1003;

	private EffectType mCurrentEffect;

	private enum EffectType {
		EF_POINTILLIZE, EF_GRAYSCALE, EF_PIXELIZE, EF_TWIRL
	}

	// private int mChosenEf;

	private ImageView mIvPreview;
	private Bitmap mBmpResized;
	private String mPath;

	private SeekBar mSbDeep, mSbRed, mSbGreen, mSbBlue;

	private ImageView mIvNone, mIvGrayscale, mIvInvert, mIvHighLight, mIvSepiaToning, mIvEmboss,
			mIvSharpen, mIvGaussianBlur, mIvMeanRemoval, mIvEngrave, mIvReflection, mIvPixelate,
			mIvTelevision, mIvLight, mIvOld, mIvRelief;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		mIvPreview = (ImageView) findViewById(R.id.ivPreview);
		mIvPreview.setOnTouchListener(this);

		mIvNone = (ImageView) findViewById(R.id.ivNone);
		mIvGrayscale = (ImageView) findViewById(R.id.ivGrayScale);
		mIvInvert = (ImageView) findViewById(R.id.ivInvert);
		mIvHighLight = (ImageView) findViewById(R.id.ivHighLight);
		mIvSepiaToning = (ImageView) findViewById(R.id.ivSepiaToning);
		mIvEmboss = (ImageView) findViewById(R.id.ivEmboss);
		mIvSharpen = (ImageView) findViewById(R.id.ivSharpen);
		mIvGaussianBlur = (ImageView) findViewById(R.id.ivGaussianBlur);
		mIvMeanRemoval = (ImageView) findViewById(R.id.ivMeanRemoval);
		mIvEngrave = (ImageView) findViewById(R.id.ivEngrave);
		mIvReflection = (ImageView) findViewById(R.id.ivReflection);
		mIvPixelate = (ImageView) findViewById(R.id.ivPixelate);
		mIvTelevision = (ImageView) findViewById(R.id.ivTelevision);
		mIvLight = (ImageView) findViewById(R.id.ivLight);
		mIvOld = (ImageView) findViewById(R.id.ivOld);
		mIvRelief = (ImageView) findViewById(R.id.ivRelief);

		mIvNone.setOnClickListener(this);
		mIvGrayscale.setOnClickListener(this);
		mIvInvert.setOnClickListener(this);
		mIvHighLight.setOnClickListener(this);
		mIvSepiaToning.setOnClickListener(this);
		mIvEmboss.setOnClickListener(this);
		mIvSharpen.setOnClickListener(this);
		mIvGaussianBlur.setOnClickListener(this);
		mIvMeanRemoval.setOnClickListener(this);
		mIvEngrave.setOnClickListener(this);
		mIvReflection.setOnClickListener(this);
		mIvPixelate.setOnClickListener(this);
		mIvTelevision.setOnClickListener(this);
		mIvLight.setOnClickListener(this);
		mIvOld.setOnClickListener(this);
		mIvRelief.setOnClickListener(this);

	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.menus, menu);
		return super.onCreateOptionsMenu(menu);
	}

	@Override
	public boolean onMenuItemSelected(int featureId, MenuItem item) {
		mCurrentEffect = null;
		
		RelativeLayout rlSepia = (RelativeLayout) findViewById(R.id.rlSepiaToning);
		rlSepia.setVisibility(View.GONE);

		RelativeLayout rlTint = (RelativeLayout) findViewById(R.id.rlTint);
		rlTint.setVisibility(View.GONE);

		RelativeLayout rlOffsetXY = (RelativeLayout) findViewById(R.id.rlOffsetXY);
		rlOffsetXY.setVisibility(View.GONE);

		switch (item.getItemId()) {
		case R.id.grayscale:
			mIvPreview.setImageBitmap(Effects.doGreyscale(mBmpResized));
			return true;

		case R.id.invert:
			mIvPreview.setImageBitmap(Effects.doInvert(mBmpResized));
			return true;

		case R.id.highlight:
			mIvPreview.setImageBitmap(Effects.doHighlightImage(mBmpResized));
			return true;

		case R.id.sepiaToning:
			rlSepia.setVisibility(View.VISIBLE);
			doSepiaToning();
			return true;

		case R.id.emboss:
			mIvPreview.setImageBitmap(Effects.emboss(mBmpResized));
			return true;

		case R.id.gaussianBlur:
			mIvPreview.setImageBitmap(Effects.applyGaussianBlur(mBmpResized));
			return true;

		case R.id.meanRemoval:
			mIvPreview.setImageBitmap(Effects.applyMeanRemoval(mBmpResized));
			return true;

		case R.id.noise:
			mIvPreview.setImageBitmap(Effects.applyNoiseEffect(mBmpResized));
			return true;

		case R.id.snow:
			mIvPreview.setImageBitmap(Effects.applySnowEffect(mBmpResized));
			return true;

		case R.id.reflection:
			mIvPreview.setImageBitmap(Effects.applyReflection(mBmpResized));
			return true;

		case R.id.engrave:
			mIvPreview.setImageBitmap(Effects.engrave(mBmpResized));
			return true;

		case R.id.tint:
			rlTint.setVisibility(View.VISIBLE);
			doTintImage();
			return true;

		case R.id.offsetXY:
			rlOffsetXY.setVisibility(View.VISIBLE);
			doOffsetXY();
			return true;

		case R.id.twirl:
			mCurrentEffect = EffectType.EF_TWIRL;
			mIvPreview.setImageBitmap(TwirlFilter.doTwirlFilter(mBmpResized,
					(float) mBmpResized.getWidth() / 2, (float) mBmpResized.getHeight() / 2));
			return true;
			
		case R.id.ripple:
			mIvPreview.setImageBitmap(RippleFilter.doRipple(mBmpResized, 0));
			return true;

		case R.id.pointillize:
			// mChosenEf = EF_POINTILLIZE;
			mCurrentEffect = EffectType.EF_POINTILLIZE;
			PointillizeFilter filter = new PointillizeFilter(4);

			// filter.setEdgeColor(Color.BLACK);
			// filter.setScale(mSizeValue);
			// filter.setRandomness(getAmout(mRandomnessValue));
			// filter.setAmount(0);
			// filter.setFuzziness(getAmout(mFuzzinessValue));
			// filter.setGridType(getSelectType());
			mIvPreview.setImageBitmap(filter.doPointillizeFilter(mBmpResized));
			return true;

		case R.id.pixelate:
			// Bitmap temp = mBmpResized.copy(Config.ARGB_8888, true);
			// AndroidImage image = new AndroidImage(temp);
			// PixelateFilter pixelate = new PixelateFilter();
			// pixelate.setPixelSize(4);
			// pixelate.process(image);
			mIvPreview.setImageBitmap(Pixelate.doPixelate(mBmpResized, 4));
			return true;

		case R.id.television:
			mIvPreview.setImageBitmap(Television.doTelevision(mBmpResized));
			return true;

		case R.id.light:
			mIvPreview.setImageBitmap(Light.doLight(mBmpResized));
			return true;

		case R.id.old:
			mIvPreview.setImageBitmap(Old.doOld(mBmpResized));
			return true;

		case R.id.brick:
			mIvPreview.setImageBitmap(Brick.doBrick(mBmpResized));
			return true;

		case R.id.relief:
			mIvPreview.setImageBitmap(Relief.doRelief(mBmpResized));
			return true;

		default:
			return super.onOptionsItemSelected(item);
		}
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		if (requestCode == REQUEST_CODE_LOAD_IMAGE && resultCode == RESULT_OK && null != data) {
			Uri selectedImage = data.getData();
			String[] filePathColumn = { MediaStore.Images.Media.DATA };
			Cursor cursor = getContentResolver().query(selectedImage, filePathColumn, null, null,
					null);
			cursor.moveToFirst();
			int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
			String picturePath = cursor.getString(columnIndex);
			cursor.close();
			mPath = picturePath;
			mBmpResized = resizeBitmap(picturePath,
					getResources().getDisplayMetrics().widthPixels / 2);
			mIvPreview.setImageBitmap(mBmpResized);

			initImagePreviewEffects(resizeBitmap(picturePath, 120));
		}
		super.onActivityResult(requestCode, resultCode, data);
	}

	public void onBtnLoadClick(View v) {
		Intent i = new Intent(Intent.ACTION_PICK,
				android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
		startActivityForResult(i, REQUEST_CODE_LOAD_IMAGE);
	}

	public void onBtnSaveClick(View v) {
		switch (mCurrentEffect) {
		case EF_POINTILLIZE:
			String root = Environment.getExternalStorageDirectory().toString();
			File myDir = new File(root + "/ImageEfTony");
			myDir.mkdirs();
			Random generator = new Random();
			int n = 10000;
			n = generator.nextInt(n);

			String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
			String fname = "Image_" + timeStamp + ".jpg";

			File file = new File(myDir, fname);
			if (file.exists()) file.delete();
			try {
				PointillizeFilter filter = new PointillizeFilter(mScale);
				Bitmap bmpOut = filter.doPointillizeFilter(BitmapFactory.decodeFile(mPath));
				FileOutputStream out = new FileOutputStream(file);
				bmpOut.compress(Bitmap.CompressFormat.JPEG, 90, out);
				out.flush();
				out.close();

			} catch (Exception e) {
				e.printStackTrace();
			}

		default:
			break;
		}
	}

	private void doTintImage() {
		SeekBar sbDegree = (SeekBar) findViewById(R.id.sbDegree);
		mIvPreview.setImageBitmap(Effects.tintImage(mBmpResized, sbDegree.getProgress()));

		sbDegree.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {

			@Override
			public void onStopTrackingTouch(SeekBar seekBar) {

			}

			@Override
			public void onStartTrackingTouch(SeekBar seekBar) {

			}

			@Override
			public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
				mIvPreview.setImageBitmap(Effects.tintImage(mBmpResized, progress));

			}
		});
	}

	private void doOffsetXY() {
		final SeekBar sbOffsetX = (SeekBar) findViewById(R.id.sbOffsetX);
		final SeekBar sbOffsetY = (SeekBar) findViewById(R.id.sbOffsetY);

		sbOffsetX.setMax(mBmpResized.getWidth());
		sbOffsetY.setMax(mBmpResized.getHeight());

		OnSeekBarChangeListener sbOffsetChange = new OnSeekBarChangeListener() {

			@Override
			public void onStopTrackingTouch(SeekBar seekBar) {
				mIvPreview.setImageBitmap(OffsetXY.doOffsetXY(mBmpResized, sbOffsetX.getProgress(),
						sbOffsetY.getProgress()));
			}

			@Override
			public void onStartTrackingTouch(SeekBar seekBar) {

			}

			@Override
			public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

			}
		};

		sbOffsetX.setOnSeekBarChangeListener(sbOffsetChange);
		sbOffsetY.setOnSeekBarChangeListener(sbOffsetChange);

	}

	private void doSepiaToning() {
		mSbDeep = (SeekBar) findViewById(R.id.sbDeep);
		mSbRed = (SeekBar) findViewById(R.id.sbRed);
		mSbGreen = (SeekBar) findViewById(R.id.sbGreen);
		mSbBlue = (SeekBar) findViewById(R.id.sbBlue);

		mSbDeep.setOnSeekBarChangeListener(mSepiaSeekBarChange);
		mSbRed.setOnSeekBarChangeListener(mSepiaSeekBarChange);
		mSbGreen.setOnSeekBarChangeListener(mSepiaSeekBarChange);
		mSbBlue.setOnSeekBarChangeListener(mSepiaSeekBarChange);

		double red = (double) mSbRed.getProgress() / 100;
		double green = (double) mSbGreen.getProgress() / 100;
		double blue = (double) mSbBlue.getProgress() / 100;
		int deep = mSbDeep.getProgress();

		mIvPreview.setImageBitmap(Effects.createSepiaToningEffect(mBmpResized, deep, red, green,
				blue));
	}

	private OnSeekBarChangeListener mSepiaSeekBarChange = new OnSeekBarChangeListener() {

		@Override
		public void onStopTrackingTouch(SeekBar seekBar) {
			// TODO Auto-generated method stub
		}

		@Override
		public void onStartTrackingTouch(SeekBar seekBar) {
			// TODO Auto-generated method stub
		}

		@Override
		public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
			double red = (double) mSbRed.getProgress() / 100;
			double green = (double) mSbGreen.getProgress() / 100;
			double blue = (double) mSbBlue.getProgress() / 100;
			int deep = mSbDeep.getProgress();

			mIvPreview.setImageBitmap(Effects.createSepiaToningEffect(mBmpResized, deep, red,
					green, blue));
		}
	};

	private int mScale = 1;

	public Bitmap resizeBitmap(String path, int requiredSize) {
		int orientation;
		try {
			// decode image size
			BitmapFactory.Options o = new BitmapFactory.Options();
			o.inJustDecodeBounds = true;
			BitmapFactory.decodeFile(path, o);

			// Find the correct scale value. It should be the power of 2.
			int width_tmp = o.outWidth, height_tmp = o.outHeight;
			mScale = 1;

			Log.i("resizeBitmap", "width_tmp:" + width_tmp + " - " + height_tmp
					+ " // requiredSize:" + requiredSize);
			while (true) {
				if (width_tmp <= requiredSize) break;
				width_tmp /= 2;
				height_tmp /= 2;
				mScale *= 2;
			}

			// decode with inSampleSize TODO
			BitmapFactory.Options o2 = new BitmapFactory.Options();
			o2.inSampleSize = mScale;
			o2.inMutable = true;
			Bitmap bm = null;
			try {
				bm = BitmapFactory.decodeFile(path, o2);
			} catch (OutOfMemoryError e1) {
				Log.e("resizebmp", "ERROR = " + e1.getMessage());
			}

			if (bm == null) {
				return bm;
			}
			Log.i("resizeBitmap", "WH: " + bm.getWidth() + ", " + bm.getHeight());

			Bitmap bitmap = bm;

			// ============================================================
			ExifInterface exif = new ExifInterface(path);

			orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);

			Matrix m = new Matrix();

			if ((orientation == ExifInterface.ORIENTATION_ROTATE_180)) {
				m.postRotate(180);
				bitmap = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), m, true);
				bm.recycle();
			} else if (orientation == ExifInterface.ORIENTATION_ROTATE_90) {
				m.postRotate(90);
				bitmap = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), m, true);
				bm.recycle();
			} else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) {
				m.postRotate(270);
				bitmap = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), m, true);
				bm.recycle();
			}

			return bitmap;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	@Override
	public void onClick(View v) {
		RelativeLayout rlSepia = (RelativeLayout) findViewById(R.id.rlSepiaToning);
		rlSepia.setVisibility(View.GONE);

		RelativeLayout rlTint = (RelativeLayout) findViewById(R.id.rlTint);
		rlTint.setVisibility(View.GONE);

		switch (v.getId()) {
		case R.id.ivNone:
			mIvPreview.setImageBitmap(mBmpResized);
			break;

		case R.id.ivGrayScale:
			mIvPreview.setImageBitmap(Effects.doGreyscale(mBmpResized));
			break;

		case R.id.ivInvert:
			mIvPreview.setImageBitmap(Effects.doInvert(mBmpResized));
			break;

		case R.id.ivHighLight:
			mIvPreview.setImageBitmap(Effects.doHighlightImage(mBmpResized));
			break;

		case R.id.ivSepiaToning:
			rlSepia.setVisibility(View.VISIBLE);
			doSepiaToning();
			break;

		case R.id.ivEmboss:
			mIvPreview.setImageBitmap(Effects.emboss(mBmpResized));
			break;

		case R.id.ivGaussianBlur:
			mIvPreview.setImageBitmap(Effects.applyGaussianBlur(mBmpResized));
			break;

		case R.id.ivMeanRemoval:
			mIvPreview.setImageBitmap(Effects.applyMeanRemoval(mBmpResized));
			break;

		case R.id.noise:
			mIvPreview.setImageBitmap(Effects.applyNoiseEffect(mBmpResized));
			break;

		case R.id.snow:
			mIvPreview.setImageBitmap(Effects.applySnowEffect(mBmpResized));
			break;

		case R.id.ivReflection:
			mIvPreview.setImageBitmap(Effects.applyReflection(mBmpResized));
			break;

		case R.id.ivEngrave:
			mIvPreview.setImageBitmap(Effects.engrave(mBmpResized));
			break;

		case R.id.ivPixelate:
			// Bitmap temp = mBmpResized.copy(Config.ARGB_8888, true);
			// AndroidImage image = new AndroidImage(temp);
			// PixelateFilter pixelate = new PixelateFilter();
			// pixelate.setPixelSize(4);
			// pixelate.process(image);
			mIvPreview.setImageBitmap(Pixelate.doPixelate(mBmpResized, 4));
			break;

		case R.id.ivTelevision:
			mIvPreview.setImageBitmap(Television.doTelevision(mBmpResized));
			break;

		case R.id.ivLight:
			mIvPreview.setImageBitmap(Light.doLight(mBmpResized));
			break;

		case R.id.ivOld:
			mIvPreview.setImageBitmap(Old.doOld(mBmpResized));
			break;

		case R.id.brick:
			mIvPreview.setImageBitmap(Brick.doBrick(mBmpResized));
			break;

		case R.id.ivRelief:
			mIvPreview.setImageBitmap(Relief.doRelief(mBmpResized));
			break;

		default:
			break;
		}

	}

	public void initImagePreviewEffects(Bitmap smallPreviewBmp) {
		mIvNone.setImageBitmap(smallPreviewBmp);
		mIvGrayscale.setImageBitmap(Effects.doGreyscale(smallPreviewBmp));
		mIvInvert.setImageBitmap(Effects.doInvert(smallPreviewBmp));
		mIvHighLight.setImageBitmap(Effects.doHighlightImage(smallPreviewBmp));
		mIvSepiaToning.setImageBitmap(Effects
				.createSepiaToningEffect(smallPreviewBmp, 150, 1, 1, 0));
		mIvSharpen.setImageBitmap(Effects.sharpen(smallPreviewBmp, 10));
		mIvEmboss.setImageBitmap(Effects.emboss(smallPreviewBmp));
		mIvGaussianBlur.setImageBitmap(Effects.applyGaussianBlur(smallPreviewBmp));
		mIvMeanRemoval.setImageBitmap(Effects.applyMeanRemoval(smallPreviewBmp));
		// mIvPreview.setImageBitmap(Effects.applyNoiseEffect(smallPreviewBmp));
		// mIvPreview.setImageBitmap(Effects.applySnowEffect(smallPreviewBmp));
		mIvReflection.setImageBitmap(Effects.applyReflection(smallPreviewBmp));
		mIvEngrave.setImageBitmap(Effects.engrave(smallPreviewBmp));
		mIvPixelate.setImageBitmap(Pixelate.doPixelate(smallPreviewBmp, 4));
		mIvTelevision.setImageBitmap(Television.doTelevision(smallPreviewBmp));
		mIvLight.setImageBitmap(Light.doLight(smallPreviewBmp));
		mIvOld.setImageBitmap(Old.doOld(smallPreviewBmp));
		// mIvPreview.setImageBitmap(Brick.doBrick(smallPreviewBmp));
		mIvRelief.setImageBitmap(Relief.doRelief(smallPreviewBmp));
	}

	@Override
	public boolean onTouch(View v, MotionEvent event) {
		if (mCurrentEffect == EffectType.EF_TWIRL) {
			float scale = (float) mBmpResized.getWidth() / v.getWidth();
			if ((float) mBmpResized.getWidth() / mBmpResized.getHeight() < (float) v.getWidth() / v.getHeight()) {
				scale = (float) mBmpResized.getHeight() / v.getHeight();
			}
			float centerX = event.getX() * scale - (float) (v.getWidth() * scale - mBmpResized.getWidth()) / 2;
			float centerY = event.getY() * scale - (float) (v.getHeight() * scale - mBmpResized.getHeight()) / 2;
			Log.i("onTouch", "event xy :" + event.getX() + "-" + event.getY() + " || " + centerX + " - " + centerY + " scale:" + scale);
			mIvPreview.setImageBitmap(TwirlFilter.doTwirlFilter(mBmpResized,
					centerX, centerY));
		}
		return false;
	}
}
